package com.easytouch.core;

import android.os.SystemClock;
import android.util.Pair;


import com.easytouch.core.encryption.Base64;
import com.easytouch.core.utils.Log;
import com.easytouch.core.utils.Tools;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Locale;

/**
 * 长链接通信类
 * Created by gulin on 2017/7/20.
 */

public class PayLongThread {

    private static PayLongThread mPayLongThread;

    private static Log mLogger; //日志

    private static boolean isStopped = true;

    private Socket socket = null;
    private InputStream in = null;
    private OutputStream out = null;

    private ReadThread mReadThread = null;
    private WriteThread mWriteThread = null;

    private static final int STATE_NONE = 0;
    private static final int STATE_SIGNING = 1;
    private static final int STATE_SIGNOK = 2;
    private static int state = STATE_NONE;

    private static LinkedList<String> mQueue = new LinkedList<String>(); // 主动指令队列

    private static ArrayList<String> mOrderList = new ArrayList<>(); //已经收到的订单号

    private String mHost = "easytouch-pay.cn"; //服务器地址

    private PayLongThread()
    {
        mLogger = new Log("PayLongThread.txt");
        mHost = Configure.getConfigData(Configure.CONFIG_PAY_IP);
        if(mHost == null || "".equals(mHost))
        {
            mHost = "easytouch-pay.cn";
        }
        mWriteThread = new WriteThread();
        mWriteThread.start();
        mReadThread = new ReadThread();
        mReadThread.start();
    }

    public static PayLongThread getInstance()
    {
        if(mPayLongThread == null)
        {
            mPayLongThread = new PayLongThread();
        }
        return mPayLongThread;
    }

    private synchronized static void appendQueueData(String data)
    {
        mQueue.offer(data);
    }

    private synchronized static String getQueueData()
    {
        return mQueue.poll();
    }

    private synchronized static int getQueueCount()
    {
        return mQueue.size();
    }

    private synchronized boolean isOrderInList(final String orderId)
    {
        return mOrderList.contains(orderId);
    }

    private synchronized void addOrderToList(final String orderId)
    {
        if(!mOrderList.contains(orderId)) {
            mOrderList.add(orderId);
        }
    }

    private synchronized void checkAndDeleteOrder()
    {
        Iterator<String> sListIterator = mOrderList.iterator();
        while(sListIterator.hasNext()){
            String e = sListIterator.next();
            if(!isOrderInTime(e, 1000 * 60 * 60)){
                sListIterator.remove();
            }
        }
    }

    /**
     * 检查订单时间是否在time之内
     * @param orderId 订单号
     * @param time 要判断的时间，单位毫秒
     * @return 是否在2分钟之内
     */
    private boolean isOrderInTime(final String orderId, final long time)
    {
        if(orderId == null || orderId.length() < 22)
        {
            return false;
        }
        String serverTime = orderId.substring(8, 22);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
        try {
            long serverTimeLong = sdf.parse(serverTime).getTime();
            if (Math.abs((new Date()).getTime() - serverTimeLong) > time) {
                return false;
            }
        }
        catch (ParseException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    public void stopThread()
    {
        isStopped = true;
    }

    private void connectServer() throws IOException,SocketException,UnknownHostException
    {
        socket = new Socket();
        socket.setSoTimeout(1000 * 60 * 5);
        InetSocketAddress socAddress = new InetSocketAddress(mHost, 6002);
        socket.connect(socAddress, 30000);
        socket.setKeepAlive(true);
        in = socket.getInputStream();
        out = socket.getOutputStream();
    }

    private boolean isConnectSuccess()
    {
        try
        {
            connectServer();
        }
        catch (UnknownHostException e)
        {
            socket = null;
            in = null;
            out = null;
            mLogger.e("pay long " + e.getMessage());
            return false;
        }
        catch (SocketException e)
        {
            socket = null;
            in = null;
            out = null;
            mLogger.e("pay long " + e.getMessage());
            return false;
        }
        catch (IOException e)
        {
            socket = null;
            in = null;
            out = null;
            mLogger.e("pay long " + e.getMessage());
            return false;
        }
        return true;
    }

    private boolean checkConnect()
    {
        if(socket != null && in != null && out != null)
        {
            if(socket.isConnected())
            {
                return true;
            }
            else
            {
                closeConnect();
                return false;
            }
        }
        else
            return false;
    }

    private boolean retryConnect()
    {
        return isConnectSuccess();
    }

    private synchronized void closeConnect()
    {
        try
        {
            if(in != null)
            {
                in.close();
            }
            if(out != null)
            {
                out.close();
            }
            if(socket != null)
            {
                socket.close();
            }
        }
        catch (IOException e1)
        {}
        socket = null;
        in = null;
        out = null;
    }

    private static boolean checkSign(final String data, final String sign)
    {
        if(data == null || "".equals(data))
        {
            return false;
        }
        if(sign == null || "".equals(sign))
        {
            return false;
        }
        if(sign.length() < 5)
        {
            return false;
        }
        String[] signArray = new String[5];
        signArray[0] = sign.substring(0, 1);
        signArray[1] = sign.substring(1, 2); //第二位
        signArray[2] = sign.substring(2, 4);
        signArray[3] = sign.substring(4, 5); //第五位
        signArray[4] = sign.substring(5);
        //交换第二位和第五位
        String temp = signArray[1];
        signArray[1] = signArray[3];
        signArray[3] = temp;
        //生成新的签名字符串
        String newSign = "";
        for (String s: signArray) {
            newSign += s;
        }
        //Base64解密
        byte[] decodeBytes = Base64.decode(newSign);
        if(decodeBytes == null)
        {
            mLogger.e("base64 decode failed");
            return false;
        }
        String newData = new String(decodeBytes);
        //mLogger.i("decode:" + newData);
        return newData.equals(data + "yichukeji2017");
//        String encode = sign(data);
//        mLogger.i("decode:" + encode);
//        return encode.equals(sign);
    }

    private static String sign(final String data)
    {
        if(data == null || "".equals(data))
        {
            return null;
        }
        String newData = data + "yichukeji2017";
        String sign = Base64.encode(newData.getBytes());//MD5.md5Encode(data); //Base64.encode(newData.getBytes()); //Base64加密
        String[] signArray = new String[5];
        signArray[0] = sign.substring(0, 1);
        signArray[1] = sign.substring(1, 2); //第二位
        signArray[2] = sign.substring(2, 4);
        signArray[3] = sign.substring(4, 5); //第五位
        signArray[4] = sign.substring(5);
        //交换第二位和第五位
        String temp = signArray[1];
        signArray[1] = signArray[3];
        signArray[3] = temp;
        //生成新的签名字符串
        String newSign = "";
        for (String s: signArray) {
            newSign += s;
        }
        return newSign;
    }

    private class ReadThread extends Thread
    {
        private boolean run = true;

        @Override
        public void run()
        {
            super.run();

            while(run)
            {
                SystemClock.sleep(100);
                if(!checkConnect())
                {
                    continue;
                }

                try
                {

                    if(in.available() <= 0)
                    {
                        continue;
                    }

                    byte[] buf = new byte[2];
                    int readCount = in.read(buf);
                    if(readCount == -1)
                    {
                        continue;
                    }
                    String sString = new String(buf);
                    if ("".equals(sString.trim()))
                    {
                        throw new SocketTimeoutException();
                    }
                    if(sString.equals("00")) //签到和心跳的应答回应
                    {
                        if(state == STATE_SIGNING)
                        {
                            //签到成功
                            state = STATE_SIGNOK;
                            mLogger.i("pay long sign ok");
                        }
                        else
                        {
                            mLogger.i("pay long heartbeat ok");
                        }
                    }
                    else if(sString.equals("12")) //付款成功通知
                    {
                        handlePay(in);
                    }
                    else if(sString.equals("13")) //出货失败应答回应
                    {
                        handleOutGoodsResult(in, false);
                    }
                    else if(sString.equals("14")) //出货成功应答回应
                    {
                        handleOutGoodsResult(in, true);
                    }
                }
                catch (SocketTimeoutException e)
                {
                    closeConnect();
                    mLogger.e("pay long timeout");
                }
                catch (IOException e)
                {
                    closeConnect();
                    mLogger.e("pay long IO error " + e.getMessage());
                }
                catch (Exception e)
                {
                    closeConnect();
                    mLogger.e("pay long data error " + e.getMessage());
                }
            }
        }

        /**
         * 处理支付成功请求
         * @param in 输入流
         * @throws IOException IO异常
         */
        private void handlePay(InputStream in) throws IOException
        {
            //先读取长度
            byte[] buf = new byte[3];
            int r = in.read(buf);
            if(r == -1)
            {
                throw new IOException("pay long read 12 error");
            }
            String sString = new String(buf);
            mLogger.i("pay long 12 length:" + sString);
            if ("".equals(sString.trim()))
            {
                throw new SocketTimeoutException();
            }
            int len = 0;
            try {
                len = Integer.parseInt(sString);
            }
            catch (NumberFormatException e)
            {
                throw new IOException("pay long read 12 number format");
            }
            //获取数据
            buf = new byte[len];
            r = in.read(buf);
            int read = r;
            while(read < len)
            {
                if(r == -1)
                {
                    throw new IOException("pay long read 12 error");
                }
                else
                {
                    r = in.read(buf, read, len - read);
                    read += r;
                }
            }
            sString = new String(buf);
            mLogger.i("pay long 12 data:" + sString);
            //验签
            String data = sString.substring(0, 36);
            String sign = sString.substring(36);
            String orderId = data.substring(0, 25); //订单号 25位
            String box = data.substring(25, 27); //柜号 2位
            String column = data.substring(27, 29); //货道号 2位
            String price = data.substring(29, 35); //价格 6位 单位分
            String type = data.substring(35, 36); //支付方式 1位
            if(checkSign(data, sign))
            {
                //验签成功
                checkAndDeleteOrder();
                if(isOrderInList(orderId))
                {
                    return;
                }
                //应答：12+订单号+2位标识（00：成功，01，验签失败）
                appendQueueData("12" + orderId + "00");
                //判断订单时间是否在2分钟内
                if(isOrderInTime(orderId, 120000)) {
                    addOrderToList(orderId);
                    outGoods(orderId, box, column, price, type);
                }
                else
                {
                    mLogger.i("order time out 2min");
                    //发送出货失败指令
                    sendOutGoodsResult(orderId, false);
                }
            }
            else
            {
                //验签失败
                mLogger.e("pay long check sign failed");
                //应答：12+订单号+2位标识（00：成功，01，验签失败）
                appendQueueData("12" + orderId + "01");
            }
        }

        private void outGoods(final String orderId, final String box, final String column, final String price, final String type)
        {
            int b,c,p;
            try {
                b = Integer.parseInt(box);
                c = Integer.parseInt(column);
                p = Integer.parseInt(price);
            }
            catch (NumberFormatException e)
            {
                mLogger.e("pay long outGoods number format error:" + e.toString());
                //发送出货失败指令
                sendOutGoodsResult(orderId, false);
                return;
            }
            String outGoodsType = DriverThread.OUT_GOODS_WEIXINPAY;
            if(type.equals("1"))
            {
                outGoodsType = DriverThread.OUT_GOODS_WEIXINPAY;
            }
            else if(type.equals("2"))
            {
                outGoodsType = DriverThread.OUT_GOODS_BAIPAY;
            }
            else if(type.equals("3"))
            {
                outGoodsType = DriverThread.OUT_GOODS_ALIPAY;
            }
            else if(type.equals("7"))
            {
                outGoodsType = DriverThread.OUT_GOODS_CCB;
            }
            else if(type.equals("0"))
            {
                //充电请求
                requestCharge(orderId);
                return;
            }
            SimpleDateFormat sd = new SimpleDateFormat("HHmmss", Locale.US);
            String tradeTrace = sd.format(new Date());
            boolean result = DriverThread.getInstance().outGoods(b, c, p, outGoodsType, tradeTrace, orderId);
            if(!result)
            {
                mLogger.e("pay long outGoods result false");
                //发送出货失败指令
                sendOutGoodsResult(orderId, false);
                return;
            }
            //DriverThread.getInstance().addOrderId(tradeTrace, orderId);
            int step = 0;
            int r = -1;
            try {
                while (step <= 120) {
                    Thread.sleep(500);
                    step++;
                    Pair<Integer, Integer> pair = DriverThread.getInstance().getResult(tradeTrace);
                    if(pair != null)
                    {
                        r = pair.first;
                        break;
                    }
                }
            } catch (InterruptedException e) {
            }
            mLogger.i("outGoods result:" + String.valueOf(r));
            if(r == 0) { // 出货成功
                //发送出货成功指令
                sendOutGoodsResult(orderId, true);
            } else { // 出货失败
                //发送出货失败指令
                sendOutGoodsResult(orderId, false);
            }
        }

        private void sendOutGoodsResult(final String orderId, final boolean isOk)
        {
            String sign = sign(orderId);
            if(sign == null)
            {
                mLogger.e("pay long sign failed");
                return;
            }
            //出货失败
            //请求：13+3位长度（不包括13和3位长度本身）+订单号（25位）+验证签名
            //出货成功
            //请求：14+3位长度（不包括14和3位长度本身）+订单号（25位）+验证签名
            String data = isOk ? "14" : "13";
            data += Tools.addLeftZero(String.valueOf(orderId.length() + sign.length()), 3);
            data += orderId;
            data += sign;
            mLogger.i(data);
            appendQueueData(data);
        }

        private void handleOutGoodsResult(InputStream in, boolean isOk) throws IOException
        {
            //获取数据
            int len = 27;
            byte[] buf = new byte[len];
            int r = in.read(buf);
            int read = r;
            while(read < len)
            {
                if(r == -1)
                {
                    throw new IOException("pay long read 13/14 error");
                }
                else
                {
                    r = in.read(buf, read, len - read);
                    read += r;
                }
            }
            String sString = new String(buf);
            mLogger.i("pay long 13/14 data:" + sString);
            //获取最后两位
            String result = sString.substring(sString.length() - 2);
            if(isOk)
            {
                if(result.equals("00"))
                {
                    mLogger.i("outGoods response ok");
                }
                else
                {
                    mLogger.i("outGoods response faild");
                }
            }
            else
            {
                if(result.equals("00"))
                {
                    mLogger.i("refund response ok");
                }
                else
                {
                    mLogger.i("refund response faild");
                }
            }
        }

        private void requestCharge(final String orderId) {
            mLogger.i("charge request");
            //先从订单号里拿到线路
            char c = orderId.charAt(orderId.length() - 1);
            int line = c - '0';
            if(line < 1 || line > ChargeThread.MAX_LINE)
            {
                mLogger.e("charge request error: unknown line:" + c);
                //发送出货失败指令
                sendOutGoodsResult(orderId, false);
                return;
            }
            mLogger.i("charge request, line:" + String.valueOf(line));
            if(ChargeThread.getInstance().requestCharge(line))
            {
                //发送出货成功指令
                sendOutGoodsResult(orderId, true);
            }
            else
            {
                //发送出货失败指令
                sendOutGoodsResult(orderId, false);
            }
        }

        public void cancelThread()
        {
            run = false;
            this.interrupt();
        }

        public boolean isRunning()
        {
            return run;
        }
    }

    private class WriteThread extends Thread {
        private boolean run = true;

        private static final long HEARTBEAT_TIME = 1000 * 10;
        private long heartbeatLastTime = 0;

        @Override
        public void run() {
            super.run();

            while (run) {
                SystemClock.sleep(100);
                if(state == STATE_NONE)
                {
                    //签到
                    if(!checkConnect())
                    {
                        if(!retryConnect())
                        {
                            state = STATE_NONE;
                            mLogger.e("pay long retry connect failed");
                        }
                        else
                        {
                            if(!doSendSignData()) //发送签到数据
                            {
                                state = STATE_NONE;
                                mLogger.e("pay long sign failed");
                            }
                        }
                    }
                    else
                    {
                        if(!doSendSignData()) //发送签到数据
                        {
                            state = STATE_NONE;
                            mLogger.e("pay long sign failed");
                        }
                    }
                }
                else if(state == STATE_SIGNING)
                {
                    if(!checkConnect()) {
                        if (!retryConnect()) {
                            state = STATE_NONE;
                        }
                    }
                }
                else if(state == STATE_SIGNOK)
                {
                    //先检查是否队列中有要发送的数据
                    if(getQueueCount() > 0)
                    {
                        if(!checkConnect())
                        {
                            if(!retryConnect())
                            {
                                mLogger.e("pay long retry connect failed");
                            }
                            else
                            {
                                String queueStr = getQueueData();
                                if(!doSendQueueData(queueStr))
                                {
                                    state = STATE_NONE;
                                    mLogger.e("pay long queue failed");
                                }
                            }
                        }
                        else
                        {
                            String queueStr = getQueueData();
                            if(!doSendQueueData(queueStr))
                            {
                                state = STATE_NONE;
                                mLogger.e("pay long queue failed");
                            }
                        }
                    }
                    else
                    {
                        //发送心跳
                        if(!checkConnect())
                        {
                            if(!retryConnect())
                            {
                                mLogger.e("pay long retry connect failed");
                            }
                            else
                            {
                                if(!doSendHeartBeatData())
                                {
                                    state = STATE_NONE;
                                    mLogger.e("pay long heartbeat failed");
                                }
                            }
                        }
                        else
                        {
                            if(!doSendHeartBeatData())
                            {
                                state = STATE_NONE;
                                mLogger.e("pay long heartbeat failed");
                            }
                        }
                    }
                }
            }
        }

        /**
         * 发送签到数据
         * @return 是否成功
         */
        private boolean doSendSignData()
        {
            //签到
            state = STATE_SIGNING;
            int retry = 3;
            boolean isSuccess = false;
            while(retry > 0)
            {
                try
                {
                    //签到通讯协议：
                    //请求：10+8位机器号
                    //应答：00
                    String sign = "10" + Configure.getMacNo();
                    mLogger.i("pay long send sign:" + sign);
                    out.write(sign.getBytes());
                    out.flush();
                    isSuccess = true;
                }
                catch (SocketTimeoutException e)
                {
                    mLogger.e("pay long send sign timeout");
                }
                catch (IOException e)
                {
                    mLogger.e("pay long send sign IO error:" + e.getMessage());
                    break;
                }
                catch (Exception e)
                {
                    closeConnect();
                    mLogger.e("pay long send sign data error:" + e.getMessage());
                    break;
                }
                if(isSuccess)
                {
                    break;
                }
                retry--;
            }
            if(isSuccess)
            {
                SystemClock.sleep(100);
                return true;
            }
            else
            {
                closeConnect();
                return false;
            }
        }

        private boolean doSendQueueData(String queueStr)
        {
            int retry = 3;
            boolean isSuccess = false;
            while(retry > 0)
            {
                try
                {
                    //发送数据
                    mLogger.i("pay long send:" + queueStr);
                    out.write(queueStr.getBytes());
                    out.flush();
                    isSuccess = true;
                    heartbeatLastTime = System.currentTimeMillis();
                }
                catch (SocketTimeoutException e)
                {
                    mLogger.e("pay long send queue timeout");
                }
                catch (IOException e)
                {
                    mLogger.e("pay long send queue IO error:" + e.getMessage());
                    break;
                }
                catch (Exception e)
                {
                    closeConnect();
                    mLogger.e("pay long send queue data error:" + e.getMessage());
                    break;
                }
                if(isSuccess)
                {
                    break;
                }
                retry--;
            }
            if(isSuccess)
            {
                SystemClock.sleep(100);
                return true;
            }
            else
            {
                closeConnect();
                return false;
            }
        }

        private boolean doSendHeartBeatData()
        {
            long currentTime = System.currentTimeMillis();
            if(currentTime - heartbeatLastTime < HEARTBEAT_TIME)
            {
                return true;
            }
            heartbeatLastTime = currentTime;
            int retry = 3;
            boolean isSuccess = false;
            while(retry > 0)
            {
                try
                {
                    //心跳通讯协议
                    //请求：11+8位机器号
                    //应答：00
                    String sign = "11" + Configure.getMacNo();
                    mLogger.i("pay long send heartbeat:" + sign);
                    out.write(sign.getBytes());
                    out.flush();
                    isSuccess = true;
                }
                catch (SocketTimeoutException e)
                {
                    mLogger.e("pay long send heartbeat timeout");
                }
                catch (IOException e)
                {
                    mLogger.e("pay long send heartbeat IO error:" + e.getMessage());
                    break;
                }
                catch (Exception e)
                {
                    closeConnect();
                    mLogger.e("pay long send heartbeat data error:" + e.getMessage());
                    break;
                }
                if(isSuccess)
                {
                    break;
                }
                retry--;
            }
            if(isSuccess)
            {
                SystemClock.sleep(100);
                return true;
            }
            else
            {
                closeConnect();
                return false;
            }
        }

        public void cancelThread() {
            run = false;
            this.interrupt();
        }

        public boolean isRunning() {
            return run;
        }
    }
}
